/*
 * Copyright (c) [2021] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <cstdint>
#include <cstdlib>
#include <memory>
#include "compiler.h"
#include "file_utils.h"
#include "mpl_timer.h"
#include "triple.h"
#include "default_options.def"

namespace maple {

std::vector<MplOption> ClangCompilerBeILP32::GetDefaultOptions(const MplOptions &options,
                                                               const Action &action) const {
  auto &triple = Triple::GetTriple();
  if (triple.GetArch() != Triple::ArchType::kAarch64Be ||
      triple.GetEnvironment() == Triple::EnvironmentType::kUnknownEnvironment) {
    CHECK_FATAL(false, "ClangCompilerBeILP32 supports only aarch64_be GNU/GNUILP32 targets\n");
  }
  std::vector<MplOption> defaultOptions;
  defaultOptions.emplace_back(MplOption("-o", action.GetFullOutputName() + ".ast"));
  defaultOptions.emplace_back(MplOption("-target", triple.Str()));
  if (triple.GetEnvironment() == Triple::EnvironmentType::kGnuIlp32) {
    defaultOptions.emplace_back(MplOption("--sysroot=" + FileUtils::SafeGetenv(kGccBeIlp32SysrootPathEnv), ""));
  } else {
    defaultOptions.emplace_back(MplOption("--sysroot=" + FileUtils::SafeGetenv(kGccBeSysrootPathEnv), ""));
  }
  defaultOptions.emplace_back(MplOption("-U__SIZEOF_INT128__", ""));
  defaultOptions.emplace_back(MplOption("-emit-ast", ""));
  for (auto item = defaultOptions.begin(); item != defaultOptions.end(); ++item) {
    item->SetValue(
        FileUtils::AppendMapleRootIfNeeded(item->GetNeedRootPath(), item->GetValue(), options.GetExeFolder()));
  }
  return defaultOptions;
}

std::string GetFormatClangPath(const MplOptions &mplOptions) {
  std::string path = mplOptions.GetExeFolder();
  // find the upper-level directory of bin/maple
  size_t index = path.find_last_of('/') - static_cast<size_t>(3);
  std::string clangPath = path.substr(0, index);
  return clangPath;
}

std::string ClangCompiler::GetBinPath(const MplOptions &mplOptions [[maybe_unused]]) const {
  std::string clangPath = "";
  if (FileUtils::SafeGetenv(kEnhancedClang) != "") {
    clangPath = FileUtils::SafeGetenv(kEnhancedClang) + "/bin/";
  } else if (FileUtils::SafeGetenv(kMapleRoot) != "") {
    clangPath = FileUtils::SafeGetenv(kMapleRoot) +
        "/tools/clang+llvm-15.0.4-x86_64-linux-gnu-ubuntu-18.04-enhanced/bin/";
  } else if (FileUtils::SafeGetenv(kClangPath) != "") {
    clangPath = FileUtils::SafeGetenv(kClangPath);
  } else {
    clangPath = GetFormatClangPath(mplOptions) +
        "thirdparty/clang+llvm-15.0.4-x86_64-linux-gnu-ubuntu-18.04-enhanced/bin/";
  }
  return clangPath;
}

const std::string &ClangCompiler::GetBinName() const {
  return kBinNameClang;
}

bool IsUseSafeOption() {
  bool flag = false;
  if (opts::boundaryStaticCheck.IsEnabledByUser() || opts::npeStaticCheck.IsEnabledByUser() ||
      opts::npeDynamicCheck.IsEnabledByUser() || opts::npeDynamicCheckSilent.IsEnabledByUser() ||
      opts::npeDynamicCheckAll.IsEnabledByUser() || opts::boundaryDynamicCheck.IsEnabledByUser() ||
      opts::boundaryDynamicCheckSilent.IsEnabledByUser() || opts::safeRegionOption.IsEnabledByUser() ||
      opts::enableArithCheck.IsEnabledByUser() || opts::defaultSafe.IsEnabledByUser()) {
    flag = true;
  }
  return flag;
}

bool JudgeOutput(const Action &action) {
  return action.GetInputFileType() != InputFileType::kFileTypeH && !opts::onlyPreprocess.IsEnabledByUser() &&
      !opts::oM.IsEnabledByUser() && !opts::oMM.IsEnabledByUser() && !opts::oMG.IsEnabledByUser() &&
      !opts::oMQ.IsEnabledByUser();
}

void FillSpecialDefaulOpt(std::vector<MplOption> &opt, const Action &action, const MplOptions &options) {
  auto &triple = Triple::GetTriple();
  if (triple.GetArch() != Triple::ArchType::kAarch64 ||
      triple.GetEnvironment() != Triple::EnvironmentType::kGnu) {
    CHECK_FATAL(false, "Use -target option to select another toolchain\n");
  }
  opt.emplace_back(MplOption("-target", triple.Str()));
  opt.emplace_back(MplOption("-isystem", GetFormatClangPath(options) + "lib/libc_enhanced/include"));
  opt.emplace_back(MplOption("-isystem", GetFormatClangPath(options) + "lib/include"));
  opt.emplace_back(MplOption("-U", "__SIZEOF_INT128__"));
  if (IsUseSafeOption()) {
    opt.emplace_back(MplOption("-DC_ENHANCED", ""));
  }
  if (opts::passO2ToClang.IsEnabledByUser() && opts::o2.IsEnabledByUser()) {
    opt.emplace_back(MplOption("-O2", ""));
  }
  if (opts::oMD.IsEnabledByUser() && (opts::compileWOLink.IsEnabledByUser() || opts::onlyCompile.IsEnabledByUser())) {
    if (opts::onlyCompile.IsEnabledByUser()) {
      opt.emplace_back(MplOption("-S", ""));
    } else {
      opt.emplace_back(MplOption("-c", ""));
    }
  }
  if (options.GetIsLto()) {
    opt.emplace_back(MplOption("-opt-string=" + options.GetOptString(), ""));
  }
  /* Set last option as -o option */
  if (JudgeOutput(action)) {
    if (!opts::linkerTimeOpt.IsEnabledByUser() || !opts::compileWOLink.IsEnabledByUser()) {
      opt.emplace_back(MplOption("-o", action.GetFullOutputName() + ".ast"));
    } else {
      opt.emplace_back(MplOption("-o", action.GetOutputName() + ".o"));
    }
    opt.emplace_back(MplOption("-emit-ast", ""));
  }
}

std::vector<MplOption> ClangCompiler::GetDefaultOptions(const MplOptions &options, const Action &action) const {
  std::vector<MplOption> defaultOptions;
  FillSpecialDefaulOpt(defaultOptions, action, options);
  for (auto item = defaultOptions.begin(); item != defaultOptions.end(); ++item) {
    item->SetValue(
        FileUtils::AppendMapleRootIfNeeded(item->GetNeedRootPath(), item->GetValue(), options.GetExeFolder()));
  }
  return defaultOptions;
}

std::unordered_set<std::string> ClangCompiler::GetFinalOutputs(const MplOptions &mplOptions [[maybe_unused]],
                                                               const Action &action) const {
  std::unordered_set<std::string> finalOutputs;
  (void)finalOutputs.insert(action.GetFullOutputName() + ".ast");
  return finalOutputs;
}

void ClangCompiler::AppendOutputOption(std::vector<MplOption> &finalOptions,
                                       const std::string &name) const {
  (void)finalOptions.emplace_back("-o", name);
}

}
